package com.grandtech.insurance.common.db.utils;

import com.grandtech.insurance.common.db.jdbc.sql.DateHelper;
import com.grandtech.insurance.common.db.jdbc.sql.TransferUtil;
import com.grandtech.insurance.common.db.jdbc.sql.annotation.Column;
import com.grandtech.insurance.common.db.jdbc.sql.annotation.Format;
import com.grandtech.insurance.common.db.jdbc.sql.annotation.Table;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.util.*;


/**
 * Created by zy on 2017/1/20.
 * 反射帮助
 */

public final class ReflectUtil {

    private ReflectUtil() {
    }

    public static List<Field> getIterationFields(Class clazz) {
        List<Field> fields = new ArrayList<>();
        Class tempClass = clazz;
        while (tempClass != null && !tempClass.getName().toLowerCase().equals("java.lang.object")) {
            fields.addAll(Arrays.asList(tempClass.getDeclaredFields()));
            tempClass = tempClass.getSuperclass();
        }
        return fields;
    }

    /**
     * 获取get方法名称
     *
     * @param field
     * @return
     */
    public static String getMethodName(Field field) {
        String fieldName = field.getName();
        String firstLetter = fieldName.substring(0, 1).toUpperCase(); // 获得和属性对应的getXXX()方法的名字
        String getMethodName = "get" + firstLetter + fieldName.substring(1); // 获得和属性对应的getXXX()方法的名字
        return getMethodName;
    }

    /**
     * 获取set方法名称
     *
     * @param field
     * @return
     */
    public static String setMethodName(Field field) {
        String fieldName = field.getName();
        String firstLetter = fieldName.substring(0, 1).toUpperCase(); // 获得和属性对应的getXXX()方法的名字
        String setMethodName = "set" + firstLetter + fieldName.substring(1); // 获得和属性对应的getXXX()方法的名字
        return setMethodName;
    }

    public static String getColumnAlias(Field field) {
        Column column = field.getAnnotation(Column.class);
        if (column != null) {
            String columnAlias = column.alias();//列名(自定义的列名称)
            if (columnAlias.equals("default")) {
                columnAlias = field.getName();//默认的列名称
            }//获取列名
            return columnAlias;
        } else {
            return field.getName();
        }
    }

    /**
     * 获取get方法名称
     *
     * @param field
     * @return
     */
    public static String getMethod(Field field) {
        String fieldName = field.getName();
        String firstLetter = fieldName.substring(0, 1).toUpperCase(); // 获得和属性对应的getXXX()方法的名字
        String getMethodName = "get" + firstLetter + fieldName.substring(1); // 获得和属性对应的getXXX()方法的名字
        return getMethodName;
    }

    /**
     * 获取set方法名称
     *
     * @param field
     * @return
     */
    public static Method setMethod(Class<?> clazz, Field field) {
        Method setMethod = null;
        try {
            String setMethodName = setMethodName(field);
            Class[] parameterTypes = new Class[1];
            parameterTypes[0] = field.getType();
            setMethod = clazz.getMethod(setMethodName, parameterTypes);
        } catch (Exception e) {
            setMethod = null;
        }
        return setMethod;
    }

    public static Method getMethod(Class<?> clazz, Field field) {
        Method getMethod = null;
        try {
            String getMethodName = getMethodName(field);
            getMethod = clazz.getMethod(getMethodName, new Class[]{});
        } catch (Exception e) {
            e.printStackTrace();
            getMethod = null;
        }
        return getMethod;
    }

    /**
     * 通过反射获取表名
     *
     * @param clazz
     * @return
     */
    public static String getTableName(Class<?> clazz) {
        Table table = clazz.getAnnotation(Table.class);
        String tableName = table.name();
        if (tableName.equals("default")) {
            tableName = clazz.getSimpleName();
        }
        return tableName;
    }

    public static String getColumnName(Field field) {
        Column column = field.getAnnotation(Column.class);
        String columnName = column.name();//列名(自定义的列名称)
        if (columnName.equals("default")) {
            columnName = field.getName();//默认的列名称
        }//获取列名
        return columnName;
    }

    /**
     * 通过反射获取主键名称(第一个主键)
     *
     * @param clazz
     * @return
     */
    public static String getFirstPrimaryKeyName(Class<?> clazz) {
        String columnName = null;
        Field[] fields = clazz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field f = fields[i];
            if (f.isAnnotationPresent(Column.class)) {
                Column column = f.getAnnotation(Column.class);
                boolean columnPKey = column.isPKey();//主键
                if (columnPKey) {
                    columnName = column.name();//列名
                    if (columnName.equals("default")) {
                        columnName = f.getName();
                    }
                    break;
                } else {
                    continue;
                }
            }
        }
        return columnName;
    }


    public static boolean isPKey(Field field) {
        Column column = field.getAnnotation(Column.class);
        Boolean columnPKey = column.isPKey();//主键
        return columnPKey;
    }

    /**
     * 通过反射获取主键值
     *
     * @param obj
     * @return
     */
    public static Object getPrimaryKeyValue(Object obj) {
        Object value = null;
        try {
            Class<? extends Object> clazz = obj.getClass();
            Field[] fields = clazz.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                if (field.isAnnotationPresent(Column.class)) {
                    Column column = field.getAnnotation(Column.class);
                    Boolean columnPKey = column.isPKey();//主键
                    if (columnPKey) {
                        String getMethodName = getMethodName(field);
                        Method getMethod = clazz.getMethod(getMethodName, new Class[]{});
                        value = getMethod.invoke(obj, new Object[]{});//获取属性值
                        break;
                    }
                } else {
                    continue;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return value;
    }

    /**
     * 通过反射获取自定义注解 主键kv;(支持复合主键)
     *
     * @param obj
     * @return
     */
    public static Map<String, Object> getPrimaryMap(Object obj) {
        Map map = new HashMap();
        try {
            Object value = null;
            Class<? extends Object> clazz = obj.getClass();
            Field[] fields = clazz.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                if (field.isAnnotationPresent(Column.class)) {
                    Column column = field.getAnnotation(Column.class);
                    String fieldName = ReflectUtil.getColumnName(field);
                    Boolean columnPKey = column.isPKey();//主键
                    if (columnPKey) {
                        String getMethodName = getMethodName(field);
                        Method getMethod = clazz.getMethod(getMethodName, new Class[]{});
                        value = getMethod.invoke(obj, new Object[]{});//获取属性值
                        map.put(fieldName, value);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }


    public static String getTopMultiPrimaries(Class clazz) {
        StringBuilder sb = new StringBuilder();
        Field[] fields = clazz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            if (field.isAnnotationPresent(Column.class)) {
                Column column = field.getAnnotation(Column.class);
                String fieldName = ReflectUtil.getColumnName(field);
                Boolean columnPKey = column.isPKey();//主键
                if (columnPKey) {
                    sb.append(fieldName).append("+");
                }
            }
        }
        sb.deleteCharAt(sb.lastIndexOf("+"));
        return sb.toString();
    }

    /**
     * 通过反射获取表名  FName as name,FName as name,FName as name
     *
     * @param clazz
     * @return
     */
    public static String getNameAsAlias(Class<?> clazz) {
        StringBuilder stringBuilder = new StringBuilder();
        Field[] fields = clazz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            if (field.isAnnotationPresent(Column.class)) {
                String columnName = queryFun(field);
                String name = field.getName();
                stringBuilder.append(columnName).append(" as ").append(name).append(",");
            }
        }
        if (stringBuilder != null && stringBuilder.toString().trim().endsWith(",")) {
            stringBuilder = stringBuilder.deleteCharAt(stringBuilder.lastIndexOf(","));
        }
        return stringBuilder.toString();
    }


    public static Field getFileByColumnName(Class clazz, String columnName) {
        try {
            Field[] fields = clazz.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                if (field.isAnnotationPresent(Column.class)) {
                    String _columnName = getColumnName(field);
                    if (_columnName.equals(columnName)) {
                        return field;
                    }
                }
            }
        } catch (Exception e) {
            return null;
        }
        return null;
    }

    /**
     * 特殊字段的函数处理
     *
     * @return
     */
    public static String saveFun(Field field, String value) {
        /**
         * 特殊函数处理
         */
        Column column = field.getAnnotation(Column.class);
        String function = column.save();
        if (!function.equals("default")) {
            value = value.substring(0, value.length() - 1);
            value = function.replace("?", value) + ",";
        }
        return value;
    }

    /**
     * 特殊字段的函数处理
     *
     * @return
     */
    public static String updateFun(Field field, String value) {
        /**
         * 特殊函数处理
         */
        Column column = field.getAnnotation(Column.class);
        String function = column.update();
        if (!function.equals("default")) {
            value = value.substring(0, value.length() - 1);
            value = function.replace("?", value) + ",";
        }
        return value;
    }

    /**
     * 特殊字段的函数处理
     *
     * @return
     */
    public static String queryFun(Field field, String value) {
        /**
         * 特殊函数处理
         */
        Column column = field.getAnnotation(Column.class);
        String function = column.query();
        if (!function.equals("default")) {
            value = function.replace("?", value);
        }
        return value;
    }

    /**
     * 特殊字段的函数处理
     *
     * @return
     */
    public static String queryFun(Field field) {
        /**
         * 特殊函数处理
         */
        Column column = field.getAnnotation(Column.class);
        String value = getColumnName(field);
        String function = column.query();
        if (!function.equals("default")) {
            value = function.replace("?", value);
        }
        return value;
    }

    /**
     * Map转 a=1 and b='2'
     *
     * @param clazz
     * @param map
     * @return
     */
    public static String map2Where(Class clazz, Map<String, Object> map) {
        String and = " and ";
        StringBuilder where = new StringBuilder();
        for (String columnName : map.keySet()) {
            Field field = ReflectUtil.getFileByColumnName(clazz, columnName);
            Class<?> type = field.getType();
            Method getMethod = ReflectUtil.getMethod(clazz, field);
            //String _columnName = ReflectUtil.getColumnName(field);
            Object value = map.get(columnName);
            if (type == Date.class) { // 属性按格式转换
                if (getMethod.isAnnotationPresent(Format.class)) { // 如果指定了
                    // 日期的转换格式则执行转换
                    Format format = getMethod.getAnnotation(Format.class);
                    String pattern = format.dateTimePattern();
                    value = DateHelper.getDAT(pattern, (Date) value);
                } else {
                    value = DateHelper.getDAT("yyyy-MM-dd HH:mm:ss", (Date) value);
                }
            }
            String item = TransferUtil.objectToKeyEqualValue(type, columnName, value);
            where.append(item);
            where.append(and);
        }
        if (where.toString().endsWith(and)) {
            where.delete(where.length() - and.length(), where.length());
        }
        return where.toString();
    }

    public static Class<?> getFieldTypeByName(Class clazz, String name) {
        if (clazz == null || name == null) {
            return null;
        }
        try {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (field.getName().equals(name)) {
                    return field.getType();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Field getFieldByName(Class clazz, String name) {
        if (clazz == null || name == null) {
            return null;
        }
        try {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (field.getName().equals(name)) {
                    return field;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @param obj
     * @param name
     * @param <T>
     * @return
     */
    public static <T> T getValByNumberVar(Object obj, String name) {
        if (obj == null) {
            return null;
        }
        try {
            Field field = getFieldByName(obj.getClass(), name);
            if (field == null) return null;
            int mod = field.getModifiers();
            if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) return null;
            field.setAccessible(true);
            Object val = field.get(obj);
            if (val == null) return null;
            return (T) val;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * @return
     */
    public static boolean hasAnnotation(Object o, String fieldName) {
        if (o == null) return false;
        Class<?> clazz = o.getClass();
        Field field = getFileByColumnName(clazz, fieldName);
        if (field != null) return true;
        return false;
    }


    public static Object getFieldByAnnotation(Object o, String fieldName) {
        if (o == null) return false;
        Class<?> clazz = o.getClass();
        Field field = getFileByColumnName(clazz, fieldName);
        if (field == null) new Throwable("field no found");
        return getValByNumberVar(o, field.getName());
    }

    public static <T> T getFieldValueT(Object obj, String fieldName) {
        Object result = null;
        Field field = ReflectUtil.getField(obj, fieldName);
        if (field != null) {
            field.setAccessible(true);
            try {
                result = field.get(obj);
                if (result == null) return null;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return (T) result;
        }
        return null;
    }

    /**
     * 利用反射获取指定对象的指定属性
     *
     * @param obj       目标对象
     * @param fieldName 目标属性
     * @return 目标属性的值
     */
    public static Object getFieldValue(Object obj, String fieldName) {
        Object result = null;
        Field field = ReflectUtil.getField(obj, fieldName);
        if (field != null) {
            field.setAccessible(true);
            try {
                result = field.get(obj);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
        return null;
    }


    /**
     * 利用反射获取指定对象里面的指定属性
     *
     * @param obj       目标对象
     * @param fieldName 目标属性
     * @return 目标字段
     */

    private static Field getField(Object obj, String fieldName) {
        Field field = null;
        for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
            try {
                field = clazz.getDeclaredField(fieldName);
                break;
            } catch (NoSuchFieldException e) {
                //这里不用做处理，子类没有该字段可能对应的父类有，都没有就返回null。
            }
        }
        return field;
    }

    /**
     * 利用反射设置指定对象的指定属性为指定的值
     *
     * @param clazz
     * @param field
     * @param val
     */
    public static void setFieldValueSmart(Object clazz, Field field, Object val) throws IllegalAccessException {
        if (val == null) return;
        Class fType = field.getType();
        Class vType = val.getClass();
        if (vType == BigDecimal.class) {
            if (fType == Integer.class || fType == int.class) {
                field.set(clazz, ((BigDecimal) val).intValue());
            } else if (fType == Float.class || fType == float.class) {
                field.set(clazz, ((BigDecimal) val).floatValue());
            } else if (fType == Double.class || fType == double.class) {
                field.set(clazz, ((BigDecimal) val).doubleValue());
            } else if (fType == Long.class || fType == long.class) {
                field.set(clazz, ((BigDecimal) val).longValue());
            } else if (fType == Short.class || fType == short.class) {
                field.set(clazz, ((BigDecimal) val).shortValue());
            }
        } else {
            if (fType == Integer.class || fType == int.class) {
                field.set(clazz, Integer.parseInt(val + ""));
            } else if (fType == Float.class || fType == float.class) {
                field.set(clazz, Float.parseFloat(val + ""));
            } else if (fType == Double.class || fType == double.class) {
                field.set(clazz, Double.parseDouble(val + ""));
            } else if (fType == Long.class || fType == long.class) {
                field.set(clazz, Long.parseLong(val + ""));
            } else if (fType == Short.class || fType == short.class) {
                field.set(clazz, Short.parseShort(val + ""));
            } else if (fType == String.class) {
                field.set(clazz, val + "");
            } else {
                field.set(clazz, val);
            }
        }
    }

    /**
     * 利用反射设置指定对象的指定属性为指定的值
     *
     * @param obj        目标对象
     * @param fieldName  目标属性
     * @param fieldValue 目标值
     */
    public static void setFieldValue(Object obj, String fieldName, Object fieldValue) {
        Field field = ReflectUtil.getField(obj, fieldName);
        if (field != null) {
            try {
                field.setAccessible(true);
                field.set(obj, fieldValue);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static Object setFieldValues(Object obj, Map<String, Object> attr) {
        Object val;
        for (String field : attr.keySet()) {
            val = attr.get(field);
            setFieldValue(obj, field, val);
        }
        return obj;
    }

    public static boolean hasGetSetMethod(Object obj) {
        if (obj == null) throw new RuntimeException("参数不能为null");
        Class clazz = obj.getClass();
        Field[] fields = obj.getClass().getDeclaredFields();//获得属性
        PropertyDescriptor pd;
        Method getMethod;
        boolean hasGetSet = false;
        for (Field field : fields) {
            try {
                pd = new PropertyDescriptor(field.getName(), clazz);
                getMethod = pd.getReadMethod();//获得get方法
                if (getMethod != null) return true;
                getMethod = pd.getWriteMethod();//获取set方法
                if (getMethod != null) return true;
            } catch (Exception e) {
                System.out.println(e);
            }
        }
        return hasGetSet;
    }
}
